import CryptoJS from 'crypto-js'
import qs from 'querystring'

interface SignParams {
  app_id: string
  timestamp: number
  hash_type: 'md5' | 'sha1'
  jsonBody?: string
  [propName: string]: any
}

interface RequestHeaders {
  'Content-Type'?: string
}

interface RequestOptions {
  url: string
  data: any
  headers: RequestHeaders,
  method: string
  access_token: string
}

const appId = '100629'
const appSecret = 'f005a4d231a145b1cfacf7be19cf429a'
const timestamp = Math.trunc(Date.now() / 1000)
const params: SignParams = {
	app_id: appId,
	timestamp,
	hash_type: 'md5'
}

function genSignParams(options: RequestOptions) {
  const { data, headers: { 'Content-Type': contentType } } = options
  const method = options.method.toUpperCase()
	const signParams: SignParams = {
    ...params,
    timestamp
  }
  const isPostOrPut = ['POST', 'PUT', 'PATCH'].includes(method)
  if (isPostOrPut && contentType && contentType.includes('multipart/form-data')) {
    return signParams
  }
  if (isPostOrPut) {
		if ((!contentType || contentType.includes('json')) && data) {
      signParams['jsonBody'] = JSON.stringify(data)
    } else if (contentType && contentType.includes('form')) {
			if (typeof data === 'string') {
				const body = qs.parse(data)
				Object.keys(body).forEach(key => {
					const value = body[key]
					signParams[key] = value
				})
			} else {
				Object.keys(data).forEach(key => {
					const value = data[key]
					signParams[key] = value
				})
			}
		}
	}
	return signParams
}

function genSWCParamsArrayForSign (options: RequestOptions) {
  const signParams = genSignParams(options)
  return Object.keys(signParams)
    .map(key => processParamsValue(signParams[key], key))
    .sort()
}

function processParamsValue (value: any, key: string) {
  if (Array.isArray(value)) {
    return value.map(item => `${key}=${item}`).sort().join('&')
  } else {
    return `${key}=${value}`
  }
}

function generateSign (options: RequestOptions) {
  let signStr = `${options.method.toUpperCase()}${options.url}?`
	const paramArray = genSWCParamsArrayForSign(options)

	paramArray.forEach(it => {
    if (it.indexOf('sign=') !== 0) {
      signStr = `${signStr}${it}&`
    }
  })

	signStr = `${signStr.substring(0, signStr.length - 1)}${appSecret}${options.access_token || ''}`

	return {
		appId,
		sign: CryptoJS.MD5(signStr).toString(),
		timestamp
	}
}

export default generateSign